#!/usr/bin/env node

/**
 * Safe publish script that skips packages that are already published
 * Usage: node safe-publish.js [--include <package-name>]
 */

const { execSync, spawnSync } = require('child_process')
const https = require('https')
const http = require('http')

/**
 * Check if a package version exists on npm registry
 */
function checkPackageExists(packageName, version) {
  return new Promise((resolve) => {
    const registryUrl = process.env.NPM_REGISTRY || 'https://registry.npmjs.org'
    const url = `${registryUrl}/${encodeURIComponent(packageName)}/${version}`

    const client = url.startsWith('https:') ? https : http

    const req = client.get(url, (res) => {
      if (res.statusCode === 200) {
        resolve(true) // Package version exists
      } else {
        resolve(false) // Package version doesn't exist
      }
    })

    req.on('error', () => {
      resolve(false) // Assume doesn't exist on error
    })

    req.setTimeout(5000, () => {
      req.destroy()
      resolve(false)
    })
  })
}

/**
 * Get list of packages that should be published from rush
 */
function getPublishablePackages(includePattern) {
  try {
    const output = execSync('rush list -p --json', { encoding: 'utf-8' })
    const config = JSON.parse(output)

    return config.projects.filter((project) => {
      // Check if package should be published according to rush.json
      const shouldPublish = project.shouldPublish === true

      // Skip if no package name
      if (!project.name) {
        return false
      }

      // If includePattern is specified, filter by it
      if (includePattern && !project.name.includes(includePattern)) {
        return false
      }

      return shouldPublish && project.name.startsWith('@hcengineering')
    })
  } catch (err) {
    console.error('Error getting package list:', err.message)
    return []
  }
}

/**
 * Publish a single package
 */
function publishPackage(packagePath, packageName) {
  try {
    console.log(`Publishing ${packageName}...`)
    execSync('npm publish', {
      cwd: packagePath,
      stdio: 'inherit',
      encoding: 'utf-8'
    })
    console.log(`✓ Successfully published ${packageName}`)
    return true
  } catch (err) {
    console.error(`✗ Failed to publish ${packageName}:`, err.message)
    return false
  }
}

/**
 * Main function
 */
async function main() {
  const args = process.argv.slice(2)
  let includePattern = null

  // Parse arguments
  for (let i = 0; i < args.length; i++) {
    if (args[i] === '--include' && i + 1 < args.length) {
      includePattern = args[i + 1]
      i++
    }
  }

  console.log('Fetching publishable packages...')
  const packages = getPublishablePackages(includePattern)

  if (packages.length === 0) {
    console.log('No packages found to publish')
    return
  }

  console.log(`Found ${packages.length} package(s) to check`)
  console.log('='.repeat(80))

  let published = 0
  let skipped = 0
  let failed = 0

  for (const pkg of packages) {
    const packageName = pkg.name
    const version = pkg.version
    const packagePath = pkg.fullPath

    console.log(`\nChecking ${packageName}@${version}...`)

    const exists = await checkPackageExists(packageName, version)

    if (exists) {
      console.log(`⊘ Skipping ${packageName}@${version} (already published)`)
      skipped++
    } else {
      console.log(`→ ${packageName}@${version} not published, publishing now...`)
      const success = publishPackage(packagePath, packageName)
      if (success) {
        published++
      } else {
        failed++
      }
    }
  }

  console.log('\n' + '='.repeat(80))
  console.log('Summary:')
  console.log(`  Published: ${published}`)
  console.log(`  Skipped:   ${skipped}`)
  console.log(`  Failed:    ${failed}`)
  console.log('='.repeat(80))

  if (failed > 0) {
    process.exit(1)
  }
}

main().catch((err) => {
  console.error('Error:', err)
  process.exit(1)
})
